<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>AI虚拟主播 - Live2D渲染器 (仅Cubism4)</title>
    <style>
        body {
            margin: 0;
            padding: 0;
            /* 纯绿色背景 - 用于OBS色度键抠像 */
            background: #00FF00;
            font-family: 'Microsoft YaHei', Arial, sans-serif;
            overflow: hidden;
        }
        
        #canvas-container {
            width: 100vw;
            height: 100vh;
            display: flex;
            justify-content: center;
            align-items: center;
        }
        
        #status {
            position: absolute;
            top: 20px;
            left: 20px;
            background: rgba(0, 0, 0, 0.7);
            color: white;
            padding: 15px 20px;
            border-radius: 10px;
            font-size: 14px;
            max-width: 300px;
            z-index: 1000;
            /* 隐藏状态面板,不影响OBS推流 */
            display: none;
        }
        
        .status-item {
            margin: 5px 0;
            display: flex;
            align-items: center;
        }
        
        .status-icon {
            width: 10px;
            height: 10px;
            border-radius: 50%;
            margin-right: 10px;
            display: inline-block;
        }
        
        .status-ok { background: #00ff00; }
        .status-error { background: #ff0000; }
        .status-loading { background: #ffff00; }
        
        #debug-info {
            position: absolute;
            top: 20px;
            right: 20px;
            background: rgba(0, 0, 0, 0.7);
            color: #0f0;
            padding: 15px 20px;
            border-radius: 10px;
            font-family: 'Consolas', monospace;
            font-size: 12px;
            max-width: 250px;
            z-index: 1000;
            /* 隐藏调试信息,不影响OBS推流 */
            display: none;
        }
        
        .debug-item {
            margin: 3px 0;
        }
        
        #subtitle {
            position: absolute;
            bottom: 50px;
            left: 50%;
            transform: translateX(-50%);
            background: rgba(0, 0, 0, 0.8);
            color: white;
            padding: 20px 40px;
            border-radius: 15px;
            font-size: 24px;
            text-align: center;
            max-width: 80%;
            z-index: 1000;
            display: none;
        }
    </style>
</head>
<body>
    <div id="canvas-container"></div>
    
    <div id="status">
        <div class="status-item">
            <span class="status-icon status-loading" id="pixi-status"></span>
            <span>PIXI.js: <span id="pixi-text">加载中...</span></span>
        </div>
        <div class="status-item">
            <span class="status-icon status-loading" id="cubism4-status"></span>
            <span>Cubism 4 Core: <span id="cubism4-text">加载中...</span></span>
        </div>
        <div class="status-item">
            <span class="status-icon status-loading" id="plugin-status"></span>
            <span>Live2D插件: <span id="plugin-text">加载中...</span></span>
        </div>
        <div class="status-item">
            <span class="status-icon status-loading" id="ws-status"></span>
            <span>WebSocket: <span id="ws-text">未连接</span></span>
        </div>
        <div class="status-item">
            <span class="status-icon status-loading" id="model-status"></span>
            <span>模型: <span id="model-text">未加载</span></span>
        </div>
    </div>
    
    <div id="debug-info">
        <div class="debug-item">FPS: <span id="fps">0</span></div>
        <div class="debug-item">口型: <span id="mouth">0.0</span></div>
        <div class="debug-item">角度X: <span id="angle-x">0.0</span></div>
        <div class="debug-item">角度Y: <span id="angle-y">0.0</span></div>
        <div class="debug-item">情感: <span id="emotion">neutral</span></div>
    </div>
    
    <div id="subtitle"></div>

    <!-- 加载本地库文件 (仅 Cubism 4) -->
    <!-- Cubism 4 运行时 -->
    <script src="/libs/live2dcubismcore.min.js?v=2"></script>
    
    <!-- PIXI.js 渲染引擎 -->
    <script src="/libs/pixi.min.js?v=2"></script>
    
    <!-- pixi-live2d-display 插件 (仅支持 Cubism 4) - v0.4.0 -->
    <script src="/libs/pixi-live2d-display-cubism4.min.js?v=2"></script>

    <script>
        // 全局变量
        let app = null;
        let model = null;
        let ws = null;
        let lastFrameTime = Date.now();
        let frameCount = 0;
        let fps = 0;

        // 更新状态显示
        function updateStatus(id, status, text) {
            const icon = document.getElementById(`${id}-status`);
            const textEl = document.getElementById(`${id}-text`);
            
            icon.className = `status-icon status-${status}`;
            if (textEl) textEl.textContent = text;
        }

        // 初始化
        async function init() {
            try {
                // 检查 PIXI
                if (typeof PIXI === 'undefined') {
                    throw new Error('PIXI.js 未加载');
                }
                updateStatus('pixi', 'ok', 'v' + PIXI.VERSION);
                console.log('✓ PIXI.js 已加载:', PIXI.VERSION);

                // 检查 Cubism 4 Core
                if (typeof Live2DCubismCore === 'undefined') {
                    throw new Error('Cubism 4 Core 未加载');
                }
                updateStatus('cubism4', 'ok', 'v' + Live2DCubismCore.Version.csmGetVersion());
                console.log('✓ Cubism 4 Core 已加载:', Live2DCubismCore.Version.csmGetVersion());

                // 检查 pixi-live2d-display 插件
                if (typeof PIXI.live2d === 'undefined') {
                    throw new Error('Live2D 插件未加载');
                }
                updateStatus('plugin', 'ok', '已加载 (Cubism4)');
                console.log('✓ pixi-live2d-display (Cubism4) 已加载');

                // 将 PIXI 暴露到 window (插件需要)
                window.PIXI = PIXI;

                // 创建 PIXI 应用
                app = new PIXI.Application({
                    width: window.innerWidth,
                    height: window.innerHeight,
                    backgroundColor: 0x00FF00,  // 纯绿色背景
                    backgroundAlpha: 1,  // 完全不透明
                    antialias: true,
                    resolution: window.devicePixelRatio || 1,
                    autoDensity: true,
                });

                document.getElementById('canvas-container').appendChild(app.view);
                console.log('✓ PIXI应用已创建');

                // 加载 Live2D 模型
                await loadModel();

                // 连接 WebSocket
                connectWebSocket();

                // 启动FPS计数
                startFPSCounter();

                // 窗口大小变化时调整
                window.addEventListener('resize', onResize);

            } catch (error) {
                console.error('初始化失败:', error);
                updateStatus('plugin', 'error', '初始化失败');
                alert('初始化失败: ' + error.message);
            }
        }

        // 加载 Live2D 模型
        async function loadModel() {
            try {
                updateStatus('model', 'loading', '加载中...');
                console.log('开始加载模型...');

                // 模型路径 (相对于HTTP服务器根目录)
                const modelPath = '/models/hiyori/runtime/hiyori_free_t08.model3.json';
                console.log('模型路径:', modelPath);

                // 加载模型
                model = await PIXI.live2d.Live2DModel.from(modelPath, {
                    autoInteract: true,  // 启用自动交互
                    autoUpdate: true,    // 启用自动更新
                });

                console.log('✓ 模型加载成功:', model);
                updateStatus('model', 'ok', '已加载');

                // 设置模型位置和大小
                // 放大模型到 1.5 倍,适合显示上半身
                const scale = Math.min(
                    window.innerWidth / model.width,
                    window.innerHeight / model.height
                ) * 2.5;  // 从 0.8 增加到 1.5

                model.scale.set(scale);
                // 水平居中
                model.x = window.innerWidth / 2;
                // 垂直位置适当下移,显示上半身 (原来是 0.5,现在改为 0.7)
                model.y = window.innerHeight * 1.3;
                // 锚点设置为模型中心
                model.anchor.set(0.5, 0.5);

                // 添加到舞台
                app.stage.addChild(model);
                console.log('✓ 模型已添加到舞台');

                // 播放待机动画
                if (model.internalModel && model.internalModel.motionManager) {
                    model.motion('idle');
                }

                // 添加点击事件
                model.on('hit', (hitAreas) => {
                    console.log('点击了:', hitAreas);
                    if (hitAreas.includes('body') || hitAreas.includes('Body')) {
                        model.motion('tap_body');
                    } else if (hitAreas.includes('head') || hitAreas.includes('Head')) {
                        model.motion('shake');
                    }
                });

            } catch (error) {
                console.error('模型加载失败:', error);
                updateStatus('model', 'error', '加载失败: ' + error.message);
                throw error;
            }
        }

        // 连接 WebSocket
        function connectWebSocket() {
            const wsUrl = 'ws://localhost:8765';
            console.log('连接 WebSocket:', wsUrl);

            ws = new WebSocket(wsUrl);

            ws.onopen = () => {
                console.log('✓ WebSocket 已连接');
                updateStatus('ws', 'ok', '已连接');
                ws.send(JSON.stringify({ type: 'ready' }));
            };

            ws.onmessage = (event) => {
                try {
                    const data = JSON.parse(event.data);
                    handleWebSocketMessage(data);
                } catch (error) {
                    console.error('解析消息失败:', error);
                }
            };

            ws.onerror = (error) => {
                console.error('WebSocket 错误:', error);
                updateStatus('ws', 'error', '连接错误');
            };

            ws.onclose = () => {
                console.log('WebSocket 断开,5秒后重连...');
                updateStatus('ws', 'error', '已断开');
                setTimeout(connectWebSocket, 5000);
            };
        }

        // 处理 WebSocket 消息
        function handleWebSocketMessage(data) {
            if (!model || !model.internalModel) return;

            const { type } = data;

            if (type === 'update_params') {
                // 更新 Live2D 参数
                const { params } = data;
                
                // 更新调试信息
                if (params.mouthOpen !== undefined) {
                    document.getElementById('mouth').textContent = params.mouthOpen.toFixed(2);
                }
                if (params.angleX !== undefined) {
                    document.getElementById('angle-x').textContent = params.angleX.toFixed(1);
                }
                if (params.angleY !== undefined) {
                    document.getElementById('angle-y').textContent = params.angleY.toFixed(1);
                }
                if (params.emotion !== undefined) {
                    document.getElementById('emotion').textContent = params.emotion;
                }

                // 应用参数到模型
                const coreModel = model.internalModel.coreModel;
                if (coreModel) {
                    // Cubism 4 参数更新
                    if (params.mouthOpen !== undefined) {
                        coreModel.setParameterValueById('ParamMouthOpenY', params.mouthOpen);
                    }
                    if (params.angleX !== undefined) {
                        coreModel.setParameterValueById('ParamAngleX', params.angleX);
                    }
                    if (params.angleY !== undefined) {
                        coreModel.setParameterValueById('ParamAngleY', params.angleY);
                    }
                    if (params.eyeLOpen !== undefined) {
                        coreModel.setParameterValueById('ParamEyeLOpen', params.eyeLOpen);
                    }
                    if (params.eyeROpen !== undefined) {
                        coreModel.setParameterValueById('ParamEyeROpen', params.eyeROpen);
                    }
                }

            } else if (type === 'speak') {
                // 显示字幕
                const { text, duration } = data;
                showSubtitle(text, duration);

            } else if (type === 'motion') {
                // 播放动作
                const { motion } = data;
                if (model.internalModel && model.internalModel.motionManager) {
                    model.motion(motion);
                }
            }
        }

        // 显示字幕
        function showSubtitle(text, duration = 3000) {
            const subtitle = document.getElementById('subtitle');
            subtitle.textContent = text;
            subtitle.style.display = 'block';

            setTimeout(() => {
                subtitle.style.display = 'none';
            }, duration);
        }

        // FPS计数器
        function startFPSCounter() {
            setInterval(() => {
                const now = Date.now();
                const delta = now - lastFrameTime;
                fps = Math.round(1000 / delta * frameCount);
                document.getElementById('fps').textContent = fps;
                
                frameCount = 0;
                lastFrameTime = now;
            }, 1000);

            app.ticker.add(() => {
                frameCount++;
            });
        }

        // 窗口大小变化
        function onResize() {
            if (!app || !model) return;

            app.renderer.resize(window.innerWidth, window.innerHeight);

            // 放大模型到 1.5 倍,适合显示上半身
            const scale = Math.min(
                window.innerWidth / model.width,
                window.innerHeight / model.height
            ) * 1.5;  // 从 0.8 增加到 1.5

            model.scale.set(scale);
            model.x = window.innerWidth / 2;
            // 垂直位置适当下移,显示上半身
            model.y = window.innerHeight * 0.7;
        }

        // 页面加载完成后初始化
        window.addEventListener('load', init);
    </script>
</body>
</html>
